package cn.highset.redis.service;

import org.springframework.data.redis.core.HyperLogLogOperations;
import org.springframework.data.redis.core.RedisTemplate;

/**
 * HyperLogLog
 *
 * @author gg
 * @version RedisHllService.java, v 0.1 2019-04-11 18:06 gg
 */
public class RedisHllService extends RedisBaseService {

    private RedisTemplate<String, Object> redisTemplate;

    public RedisHllService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 对应命令 pfAdd
     * 将任意数量的元素添加到指定的 HyperLogLog 里面。
     * 作为这个命令的副作用， HyperLogLog 内部可能会被更新， 以便反映一个不同的唯一元素估计数量（也即是集合的基数）。
     * 如果 HyperLogLog 估计的近似基数（approximated cardinality）在命令执行之后出现了变化， 那么命令返回 1 ， 否则返回 0 。
     * 如果命令执行时给定的键不存在， 那么程序将先创建一个空的 HyperLogLog 结构， 然后再执行命令
     *
     * @param key    must not be {@literal null}.
     * @param values must not be {@literal null}.
     * @return 整数回复： 如果 HyperLogLog 的内部储存被修改了， 那么返回 1 ， 否则返回 0
     */
    public Long pfAdd(final String key, Object... values) {
        checkKey(key);
        HyperLogLogOperations<String, Object> hyperLogLogOperations = redisTemplate.opsForHyperLogLog();
        return hyperLogLogOperations.add(key, values);
    }

    /**
     * 对应命令 pfCount
     * 当 pfCount key [key …] 命令作用于单个键时， 返回储存在给定键的 HyperLogLog 的近似基数， 如果键不存在， 那么返回 0 。
     * 当 pfCount key [key …] 命令作用于多个键时， 返回所有给定 HyperLogLog 的并集的近似基数，
     * 这个近似基数是通过将所有给定 HyperLogLog 合并至一个临时 HyperLogLog 来计算得出的。
     * 通过 HyperLogLog 数据结构， 用户可以使用少量固定大小的内存， 来储存集合中的唯一元素
     * （每个 HyperLogLog 只需使用 12k 字节内存，以及几个字节的内存来储存键本身）。
     * 命令返回的可见集合（observed set）基数并不是精确值， 而是一个带有 0.81% 标准错误（standard error）的近似值
     *
     * @param keys must not be {@literal null} or {@literal empty}.
     * @return 整数回复： 给定 HyperLogLog 包含的唯一元素的近似数量
     */
    public Long pfCount(final String... keys) {
        HyperLogLogOperations<String, Object> hyperLogLogOperations = redisTemplate.opsForHyperLogLog();
        return hyperLogLogOperations.size(keys);
    }

    /**
     * 对应命令 pfMerge
     * 将多个HyperLogLog合并（合并）为一个HyperLogLog，合并后的Hy​​perLogLog的基数接近于所有输入HyperLogLog的可见集合（observe set）的并集。
     * 合并得出的HyperLogLog会被储存在destination键里面，如果该键并不存在，那么命令在执行之前，会先为该键创建一个空的HyperLogLog
     *
     * @param destination key of HyperLogLog to move source keys into.
     * @param sourceFinal StringKeys must not be {@literal null} or {@literal empty}.
     */
    public Long union(final String destination, final String... sourceFinal) {
        checkDestKey(destination);
        HyperLogLogOperations<String, Object> hyperLogLogOperations = redisTemplate.opsForHyperLogLog();
        return hyperLogLogOperations.union(destination, sourceFinal);
    }
}